#!/usr/bin/env python
#
# Copyright (C) 2005-2020 ABINIT Group (Yann Pouillon)
#
# This file is part of the ABINIT software package. For license information,
# please see the COPYING file in the top-level directory of the ABINIT source
# distribution.
#
from __future__ import print_function, division, absolute_import #, unicode_literals

try:
    from ConfigParser import ConfigParser
except ImportError:
    from configparser import ConfigParser
from time import gmtime,strftime

import os
import re
import sys

class MyConfigParser(ConfigParser):

  def optionxform(self,option):
    return str(option)

# ---------------------------------------------------------------------------- #

#
# Subroutines
#

# Makefile header
def makefile_header(name,stamp):

  return """#
# Makefile for ABINIT                                      -*- Automake -*-
# Generated by %s on %s

#
# IMPORTANT NOTE
#
# Any manual change to this file will systematically be overwritten.
# Please modify the %s script or its config file instead.
#

""" % (name,stamp,name)



def makefile_subdirs(filename,prefix):

  ret       = ""
  re_begin  = re.compile("^## BEGIN BUILD$")
  re_end    = re.compile("^## END BUILD$")
  tmp       = open(filename, "rt").readlines()
  save_line = False

  for line in tmp:
    if ( re_end.match(line) ):
      save_line = False
    if ( save_line ):
      ret += line
    if ( re_begin.match(line) ):
      save_line = True

  ret = re.sub("#","",ret).strip().split()
  ret = ["$(%s%s)" % (prefix,x) for x in ret]

  return ret



# ---------------------------------------------------------------------------- #

#
# Main program
#

# Initial setup
my_name    = "make-makefiles-inter"
my_configs = {
  "libs":"config/specs/corelibs.conf",
  "bsys":"config/specs/buildsys.conf",
}

mf_sep = "\n# ---------------------------------------------------------------------------- #\n"

# Check if we are in the top of the ABINIT source tree
if ( not os.path.exists("configure.ac") or
     not os.path.exists("src/98_main/abinit.F90") ):
  print("%s: You must be in the top of an ABINIT source tree." % my_name)
  print("%s: Aborting now." % my_name)
  sys.exit(1)

# Read config file(s)
for cnf_file in my_configs.values():
  if ( os.path.exists(cnf_file) ):
    if ( re.search("\.cf$",cnf_file) ):
      exec(compile(open(cnf_file).read(), cnf_file, 'exec'))
  else:
    print("%s: Could not find config file (%s)." % (my_name,cnf_file))
    print("%s: Aborting now." % my_name)
    sys.exit(2)

# What time is it?
now = strftime("%Y/%m/%d %H:%M:%S +0000",gmtime())

# Parse configuration
cnf = MyConfigParser()
cnf.read(my_configs["libs"])
abinit_parents = list(set([cnf.get(item, "parent") for item in cnf.sections()]))

# Process available libraries, one parent at a time
for parent in abinit_parents:

  # Select parent dir
  if ( parent == "common" ):
    par_dir = os.path.join("shared", "common", "src")
  elif ( parent == "libpaw" ):
    par_dir = os.path.join("shared", "libpaw")
  else:
    par_dir = "src"

  # Select subdirs to process
  abinit_libs = sorted([item for item in cnf.sections() \
    if ( cnf.get(item, "parent") == parent )])

  # Extract information for each subdir
  lib_specs = {
    "abirules": [],
    "optional": [],
    "subdirs": [],
  }
  lib_specs["subdirs"] += ["incs", "mods"]
  for lib in abinit_libs:
    if ( parent == "libpaw" ):
      subdir = "src"
    else:
      subdir = lib
    if ( cnf.get(lib, "abirules") == "yes" ):
      lib_specs["abirules"].append(subdir)
    if ( cnf.get(lib, "optional") == "yes" ):
      lib_specs["optional"].append(subdir)
      lib_specs["subdirs"].append("$(%s_%s)" % (parent, subdir))
    else:
      lib_specs["subdirs"].append(subdir)
  if ( parent == "core" ):
    lib_specs["abirules"].append("98_main")
    lib_specs["subdirs"].append("98_main")

  # Start Makefile.am
  makefile = makefile_header(my_name, now)

  # Declare optional subdirs
  if ( len(lib_specs["optional"]) > 0):
    makefile += "# Optional subdirectories\n"
    for subdir in lib_specs["optional"]:
      makefile += "if DO_BUILD_{NAME}\n  {parent}_{name} = {name}\nelse\n  {parent}_{name} =\nendif\n".format(NAME=subdir.upper(), name=subdir, parent=parent)

  # List all subdirectories
  if ( len(lib_specs["subdirs"]) > 0 ):
    makefile += "\n# Registered subdirectories\nSUBDIRS = \\\n  "
    makefile += " \\\n  ".join(lib_specs["subdirs"]) + "\n"

  # Add an abirules target
  if ( len(lib_specs["abirules"]) > 0 ):
    makefile += mf_sep
    makefile += """
# Enforce ABINIT Coding Style (the so-called ABIRULES)
#
# Note concerning abirules (as of 5.8.0)
# The order of config.h vs abi_common.h is not preserved.
# This should be corrected by hand. It is to be done, at present,
# in get_irredg, merge_kgirr, setshells, sigma
"""
    makefile += "abirules:\n\t@echo 'Reporting possible errors in the abirules procedure' >tmp-abirules.log\n"
    for subdir in lib_specs["abirules"]:
      makefile += "\t@$(PERL) ../developers/maintainers/abirules.pl -d %s >>tmp-abirules.log\n" % (subdir)
    makefile += "\t@grep 'Error' tmp-abirules.log\n"

  # Add a help target
  makefile += mf_sep
  makefile += "\n# Help\n"
  makefile += "help:\n\t@cat $(top_srcdir)/doc/help_make/help_make_{}.txt\n".format(parent)

  # Add a parents target
  makefile += mf_sep
  makefile += "\n# Creation of parent subroutines\n"
  makefile += "parents:\n\t$(PERL) $(top_srcdir)/developers/maintainers/parents.pl -s\n"
  makefile +="\trm -f *.old */*.old\n"

  # Add a local maintainer-clean target
  makefile += mf_sep
  makefile += "\n# Thorough clean-up\nmaintainer-clean-local:\n\trm -f tmp-abirules.log\n"
  for file_ext in ["old", "orig", "rej"]:
    makefile += "\tfind . -name '*.%s' -exec rm {} \\;\n" % file_ext

  # Include additional information
  mf_add = os.path.join("config", "makefiles", "{}.am".format(parent))
  if ( os.path.exists(mf_add) ):
    makefile += mf_sep
    makefile += "\n"
    makefile += open(mf_add, "r").read()

  # Write down Makefile.am
  with open(os.path.join(par_dir, "Makefile.am"), "w") as mf_parent:
    mf_parent.write(makefile)

  # Write Makefile.am for includes
  if ( os.path.exists(os.path.join(par_dir, "incs")) ):
    includes = []
    inc_specs = os.path.join(par_dir, "incs", "abinit.src")
    with open(inc_specs, "r") as src_cnf:
      exec(compile(src_cnf.read(), inc_specs, 'exec'))
    with open(os.path.join(par_dir, "incs", "Makefile.am"), "w") as mf_incs:
      inc_text  = makefile_header(my_name, now)
      inc_text += "all-local:\n\t@echo \"There is no buildable file here\"\n\n"
      if ( len(includes) > 0 ):
        inc_text += "EXTRA_DIST = \\\n\t" + " \\\n\t".join(includes) + "\n"
      mf_incs.write(inc_text)

  # Write Makefile.am for modules
  if ( os.path.exists(os.path.join(par_dir, "mods")) ):
    with open(os.path.join(par_dir, "mods", "Makefile.am"), "w") as mf_mods:
      mod_text  = makefile_header(my_name, now)
      mod_text += "all-local:\n\t@echo \"There is no buildable file here\"\n\n"
      mod_text += "mostlyclean-local:\n\trm -f *.$(MODEXT) *.h\n"
      mf_mods.write(mod_text)

# Write shared intermediate Makefile.am files
with open("shared/Makefile.am", "w") as mf_shared:
  mf_shared.write(makefile_header(my_name, now))
  mf_shared.write("""\
SUBDIRS = .
if DO_BUILD_SHARED_COMMON
  SUBDIRS += common
endif
if DO_BUILD_SHARED_LIBPAW
  SUBDIRS += libpaw
endif
""")
with open("shared/common/Makefile.am", "w") as mf_common:
  mf_common.write(makefile_header(my_name, now))
  mf_common.write("SUBDIRS = src\n")

# Write makefiles for subsystems
cnf_bsys = MyConfigParser()
cnf_bsys.read(my_configs["bsys"])
for subsys in cnf_bsys.sections():
  if ( (cnf_bsys.get(subsys, "mode") == "detached") and \
       (cnf_bsys.get(subsys, "type") == "data") ):
    sub_path = os.path.join("config", "dist", "auto-%s.lst" % subsys)
    if ( os.path.exists(sub_path) ):
      sub_idx = len(subsys) + 1
      sub_files = [item[sub_idx:].strip() for item in open(sub_path, "r").readlines()]
      with open(os.path.join(subsys, "Makefile.am"), "w") as mf_sub:
        mf_sub.write(makefile_header(my_name, now))
        mf_sub.write("EXTRA_DIST = \\\n  " + " \\\n  ".join(sub_files) + "\n")
